<?php
// $Id$

/**
 * Mail_Address_Plan 封装来自 mail_address_plan 数据表的记录及领域逻辑
 */
class Mail_Address_Plan extends QDB_ActiveRecord_Abstract
{
    const
    PLAN_STATUS_PROCESSING = 1,
    PLAN_STATUS_PAUSED = 2;

    function saveCfg($data)
    {
        if ($this->status == 1 && isset($data['status']) && $data['status'] == 2) {
            $this->status = 2;
        } elseif (isset($data['status']) && $data['status'] == 1 && isset($data['start_date']) && isset($data['next_tpl_num'])) {

            if (empty($data['start_date'])) throw new Exception('请填入开始时间');
            if (empty($data['next_tpl_num'])) throw new Exception('请确定从哪个模板开始');

            $this->status = 1;
            $this->start_date = $data['start_date'];
            $this->next_tpl_num = $data['next_tpl_num'];
        }

        if (isset($data['mail_address_id'])) {
            $this->mail_address_id = $data['mail_address_id'];
        }

        $this->save();

        if($this->status == 2){
            foreach($this->waitingList as $waitingMail){
                $waitingMail->destroy();
            }
        }else{
            $cfg = array(
                'start_date' => $this->start_date,
                'next_tpl_index' => $this->next_tpl_index,
            );
            $this->genMails($cfg);
        }
    }

    static function findById($id)
    {
        return self::find('mail_address_plan_id=?', $id);
    }

    function planName()
    {
        return $this->plan->name;
    }

    function completeSend()
    {
        $this->last_tpl_send_datetime = date('Y-m-d H:i:s');

        $nextTplIndex = $this->nextTplIndex($this->next_tpl_index);

        if ($nextTplIndex === false) {
            $this->next_tpl_index = -1;
        } else {
            if ($nextTplIndex <= $this->next_tpl_index) {
                $this->loop_cnt++;
            }
            $this->next_tpl_index = $nextTplIndex;
        }

        $this->save();

        $this->genMails();
    }

    function nextTplIndexByTplId($baseTplId)
    {
        return $this->nextTplIndex($this->tplIndexByTplId($baseTplId));
    }

    function tplIndexByTplId($tplId)
    {
        $map = $this->plan->tpls->values('mail_tpl_id');
        $map = array_flip($map);
        return $map[$tplId];
    }

    function nextTplIndex($baseTplIndex)
    {
        $nextTplIndex = $baseTplIndex + 1;

        if ($nextTplIndex >= $this->tpls()->count()) {
            if ($this->loop_tpl_index() >= 0) {
                $nextTplIndex = $this->loop_tpl_index();
            } else {
                return false;
            }
        }

        return $nextTplIndex;
    }

    function loop_tpl_index()
    {
        return $this->plan->loop_tpl_index;
    }

    function tpls()
    {
        return $this->plan->tpls;
    }

    protected function _genNextMailSendDate($baseSendDate, $nextTplIndex)
    {
        $nextPlanTpl = $this->plan->tpls[$nextTplIndex];
        $intervalPeriods = $nextPlanTpl->interval_periods;
        if (!is_int($intervalPeriods) || $intervalPeriods < -1) throw new Exception('invalid interval periods for mail templates');

        if($intervalPeriods === 0) return $baseSendDate;

        $lastDate = $baseSendDate;
        list($yLastDate, $mLastDate, $dLastDate) = explode('-', $lastDate);

        $workDays = Mail_Period::workDays();
        $restDays = Mail_Period::restDays();

        $cntPeriods = 0;
        $dateDiff = 0;
        while ($cntPeriods < $intervalPeriods) {
            $dateDiff++;
            $timeStampCurrentDate = mktime(0, 0, 0, $mLastDate, $dLastDate + $dateDiff, $yLastDate);

            switch ($nextPlanTpl->period_type) {
                case Mail_Period::PERIOD_TYPE_DAY:
                    $cntPeriods++;
                    break;
                case Mail_Period::PERIOD_TYPE_WORKDAY:
                    if (in_array(date('w', $timeStampCurrentDate), $workDays)) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_RESTDAY:
                    if (in_array(date('w', $timeStampCurrentDate), $restDays)) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_MONDAY:
                    if (date('w', $timeStampCurrentDate) == 1) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_TUESDAY:
                    if (date('w', $timeStampCurrentDate) == 2) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_WEDNESDAY:
                    if (date('w', $timeStampCurrentDate) == 3) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_THURSDAY:
                    if (date('w', $timeStampCurrentDate) == 4) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_FRIDAY:
                    if (date('w', $timeStampCurrentDate) == 5) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_SATURDAY:
                    if (date('w', $timeStampCurrentDate) == 6) {
                        $cntPeriods++;
                    }
                    break;
                case Mail_Period::PERIOD_TYPE_SUNDAY:
                    if (date('w', $timeStampCurrentDate) == 0) {
                        $cntPeriods++;
                    }
                    break;
            }
        }
        return date('Y-m-d', $timeStampCurrentDate);
    }

    /**
     * generate mails in conditions
     * @param null $data
     * @return
     */
    function genMails($data = null)
    {

        if(isset($data['qtyToGen'])) $qtyToGen = $data['qtyToGen'];
        else $qtyToGen = Mail_Waiting::MIN_QTY_PER_ADDRESS - $this->waitingList->count();
        if ($qtyToGen <= 0) return;

        $ret = array();

        if (isset($data['start_date']) && isset($data['next_tpl_index'])) {
            if(!isset($this->plan->tpls[$data['next_tpl_index']])) throw new Exception('模板号错误！邮件计划['.$this->plan->name.']总共有'.$this->plan->tpls->count().'个模板');

            $ret[] = $this->genStartMail($data['start_date'], $data['next_tpl_index']);
            $qtyToGen--;
        }

        $lastWaiting = $this->lastWaiting();

        while ($qtyToGen > 0) {

            $newWaiting = $this->genNextMail($lastWaiting);
            $ret[] = $newWaiting;
            if ($newWaiting === false) return;

            $qtyToGen--;
            $lastWaiting = $newWaiting;
        }

        return $ret;
    }

    function lastWaiting()
    {
        return $this->waitingList->last();
    }

    function genStartMail($startDate, $nextTplIndex)
    {
        $nextTpl = $this->plan->tpls[$nextTplIndex];

        $body = $this->_replaceMailContent($nextTpl->body());

        $newWaiting = new Mail_Waiting(
            array(
                 'from' => $nextTpl->from() ? $nextTpl->from() : Q::ini('appini/mail/username'),
                 'to' => $this->address(),
                 'subject' => $nextTpl->subject(),
                 'body' => $body,
                 'send_time' => $nextTpl->send_time(),
                 'send_date' => $startDate,
                 'customer_id' => $this->customer_id,
                 'mail_address_id' => $this->mail_address_id,
                 'mail_plan_id' => $this->mail_plan_id,
                 'mail_tpl_id' => $nextTpl->mail_tpl_id,
            )
        );
        $newWaiting->attachments = $nextTpl->attachments();
        $newWaiting->save();

        $this->waitingList->push($newWaiting);
        return $newWaiting;
    }

    function _replaceMailContent($body){
        return str_replace('{customer_nick_name}',$this->customer_name(),$body);
    }

    function genNextMail($baseMailWaiting)
    {

        $nextTplIndex = $this->nextTplIndexByTplId($baseMailWaiting->mail_tpl_id);
        if ($nextTplIndex === false) return false;

        $nextTpl = $this->plan->tpls[$nextTplIndex];
        $sendDate = $this->_genNextMailSendDate($baseMailWaiting->send_date, $nextTplIndex);

        // replacement
        $body = $this->_replaceMailContent($nextTpl->body());

        $newWaiting = new Mail_Waiting(
            array(
                 'from' => $nextTpl->from() ? $nextTpl->from() : Q::ini('appini/mail/username'),
                 'to' => $this->address(),
                 'subject' => $nextTpl->subject(),
                 'body' => $body,
                 'send_time' => $nextTpl->send_time(),
                 'send_date' => $sendDate,
                 'customer_id' => $this->customer_id,
                 'mail_address_id' => $this->mail_address_id,
                 'mail_plan_id' => $this->mail_plan_id,
                 'mail_tpl_id' => $nextTpl->mail_tpl_id,
            )
        );
        $newWaiting->attachments = $nextTpl->attachments();
        $newWaiting->save();

        $this->waitingList->push($newWaiting);
        return $newWaiting;
    }

    function nextTpl($baseTplIndex)
    {
        $nextTplIndex = $this->nextTplIndex($baseTplIndex);
        if ($nextTplIndex === false) return false;

        return $this->plan->tpls[$nextTplIndex];
    }

    function address()
    {
        return $this->address->address;
    }

    function customer_name()
    {
        return $this->address->customer_name;
    }

    protected function _setNextTplNum($val)
    {
        $this->next_tpl_index = $val - 1;
    }

    protected function _getNextTplNum()
    {
        return $this->next_tpl_index + 1;
    }


    /**
     * 返回对象的定义
     *
     * @static
     *
     * @return array
     */
    static function __define()
    {
        return array
        (
            // 指定该 ActiveRecord 要使用的行为插件
            'behaviors' => '',

            // 指定行为插件的配置
            'behaviors_settings' => array
            (
                # '插件名' => array('选项' => 设置),
            ),

            // 用什么数据表保存对象
            'table_name' => 'mail_address_plan',

            // 指定数据表记录字段与对象属性之间的映射关系
            // 没有在此处指定的属性，QeePHP 会自动设置将属性映射为对象的可读写属性
            'props' => array
            (
                // 主键应该是只读，确保领域对象的“不变量”
                'mail_plan_id' => array('readonly' => true),

                /**
                 *  可以在此添加其他属性的设置
                 */
                'next_tpl_num' => array('setter' => '_setNextTplNum', 'getter' => '_getNextTplNum'),

                /**
                 * 添加对象间的关联
                 */
                'address' => array('belongs_to' => 'Mail_Address'),
                'plan' => array('belongs_to' => 'Mail_Plan'),
                'waitingList' => array('has_many' => 'Mail_Waiting', 'source_key' => array('mail_address_id', 'mail_plan_id'), 'target_key' => array('mail_address_id', 'mail_plan_id')),

            ),

            /**
             * 允许使用 mass-assignment 方式赋值的属性
             *
             * 如果指定了 attr_accessible，则忽略 attr_protected 的设置。
             */
            'attr_accessible' => '',

            /**
             * 拒绝使用 mass-assignment 方式赋值的属性
             */
            'attr_protected' => '',

            /**
             * 指定在数据库中创建对象时，哪些属性的值不允许由外部提供
             *
             * 这里指定的属性会在创建记录时被过滤掉，从而让数据库自行填充值。
             */
            'create_reject' => '',

            /**
             * 指定更新数据库中的对象时，哪些属性的值不允许由外部提供
             */
            'update_reject' => '',

            /**
             * 指定在数据库中创建对象时，哪些属性的值由下面指定的内容进行覆盖
             *
             * 如果填充值为 self::AUTOFILL_TIMESTAMP 或 self::AUTOFILL_DATETIME，
             * 则会根据属性的类型来自动填充当前时间（整数或字符串）。
             *
             * 如果填充值为一个数组，则假定为 callback 方法。
             */
            'create_autofill' => array
            (
                # 属性名 => 填充值
                'status' => self::PLAN_STATUS_PAUSED,
                'loop_cnt' => 0,
                'next_tpl_index' => 0
            ),

            /**
             * 指定更新数据库中的对象时，哪些属性的值由下面指定的内容进行覆盖
             *
             * 填充值的指定规则同 create_autofill
             */
            'update_autofill' => array
            (
            ),

            /**
             * 在保存对象时，会按照下面指定的验证规则进行验证。验证失败会抛出异常。
             *
             * 除了在保存时自动验证，还可以通过对象的 ::meta()->validate() 方法对数组数据进行验证。
             *
             * 如果需要添加一个自定义验证，应该写成
             *
             * 'title' => array(
             *        array(array(__CLASS__, 'checkTitle'), '标题不能为空'),
             * )
             *
             * 然后在该类中添加 checkTitle() 方法。函数原型如下：
             *
             * static function checkTitle($title)
             *
             * 该方法返回 true 表示通过验证。
             */
            'validations' => array
            (
                'status' => array
                (
                    array('is_int', 'status必须是一个整数'),

                ),

                'loop_cnt' => array
                (
                    array('is_int', 'loop_cnt必须是一个整数'),

                ),

                'next_tpl_index' => array
                (
                    array('is_int', 'next_tpl_index必须是一个整数'),

                ),


            ),
        );
    }


/* ------------------ 以下是自动生成的代码，不能修改 ------------------ */

    /**
     * 开启一个查询，查找符合条件的对象或对象集合
     *
     * @static
     *
     * @return QDB_Select
     */
    static function find()
    {
        $args = func_get_args();
        return QDB_ActiveRecord_Meta::instance(__CLASS__)->findByArgs($args);
    }

    /**
     * 返回当前 ActiveRecord 类的元数据对象
     *
     * @static
     *
     * @return QDB_ActiveRecord_Meta
     */
    static function meta()
    {
        return QDB_ActiveRecord_Meta::instance(__CLASS__);
    }


/* ------------------ 以上是自动生成的代码，不能修改 ------------------ */

}

class NoMoreTpl_Exception extends Exception
{
}